package jftp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FtpConnection extends Thread // 继承线程
{
	static public String root = null;
	/** 主目录 */
	private String currentDir = "/"; // 当前目录
	private Socket socket; // 客户套接字
	private BufferedReader reader = null; // 读入的字符缓冲区
	private BufferedWriter writer = null; // 写出的字符缓冲区
	private String clientIP = null; // 客户端IP地址
	private Socket tempSocket = null; // tempSocket用于传送文件
	private ServerSocket pasvSocket = null; // 用于被动模式
	private String host = null;
	private int port = (-1);

	public FtpConnection(Socket socket) {
		this.socket = socket;
		this.clientIP = socket.getInetAddress().getHostAddress();// 获取客户端IP地址
	}

	public void run() {
		String command;
		try {
			System.out.println(clientIP + " connected.");
			socket.setSoTimeout(60000); // ftp超时设定
			reader = new BufferedReader(new InputStreamReader(socket
					.getInputStream()));// 获取输入输出流
			writer = new BufferedWriter(new OutputStreamWriter(socket
					.getOutputStream()));
			response("220-Welcome message......"); // *****自定义
			response("220-Welcome message......"); // *****自定义
			response("220 Notice:last msg");
			for (;;) // 循环检测命令
			{
				command = reader.readLine(); // 读入内容
				if (command == null)
					break;
				System.out.println("Command come from  " + clientIP + " Command is: " + command);
				parseCommand(command); // *****自定义
				if (command.equals("QUIT")) // 收到QUIT命令
					break;
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (reader != null)
					reader.close(); // 关闭输入流
			} catch (Exception e) {
			}
			try {
				if (writer != null)
					writer.close(); // 关闭输出流
			} catch (Exception e) {
			}
			try {
				if (this.pasvSocket != null)
					pasvSocket.close(); // 关闭被动模式
			} catch (Exception e) {
			}
			try {
				if (this.tempSocket != null)
					tempSocket.close(); // 关闭空套接字
			} catch (Exception e) {
			}
			try {
				if (this.socket != null)
					socket.close(); // 关闭当前套接字
			} catch (Exception e) {
			}
		}
		System.out.println(clientIP + " Disconnnect......");
	}

	private void response(String s) throws Exception // 自定义：向客户发送消息
	{
		// System.out.println("  [RESPONSE] "+s);
		writer.write(s);
		writer.newLine();
		writer.flush(); // 注意要flush否则响应仍在缓冲区
	}

	private String getParam(String cmd, String start) // 自定义：获取参数
	{
		String s = cmd.substring(start.length(), cmd.length());
		return s.trim();
	}

	private String translatePath(String path) // 自定义：获取路径
	{
		if (path == null)
			return root;
		if (path.equals(""))
			return root;// 为空，返回主目录
		path = path.replace('/', '\\');// 替换
		return root + path;
	}

	// 获取文件长度，注意是一个字符串，并定义为标准的12个字符
	private String getFileLength(long length) {
		String s = Long.toString(length);
		int spaces = 12 - s.length();
		for (int i = 0; i < spaces; i++)
			s = " " + s;
		return s;
	}

	// 接下来便是处理用户命令，这个方法有点长，需要重构一下，我只是把LIST命令单独挪了出来：

	private void parseCommand(String s) throws Exception {
		if (s == null || s.equals(""))
			return;
		if (s.startsWith("USER ")) {
			response("331 Please input password!");
		} else if (s.startsWith("PASS ")) {
			response("230 Welcome!");
		} else if (s.equals("QUIT")) {
			response("221 Byebye！");
		} else if (s.equals("TYPE A")) {
			response("200 TYPE set to A.");
		} else if (s.equals("TYPE I")) {
			response("200 TYPE set to I.");
		} else if (s.equals("NOOP")) {
			response("200 NOOP OK.");
		} else if (s.startsWith("CWD")) { // 设置当前目录，注意没有检查目录是否有效
			this.currentDir = getParam(s, "CWD ");// 取CWD后的字符，即当前目录
			response("250 CWD Command execute success.");
		} else if (s.startsWith("CDUP")) {
			int n = this.currentDir.lastIndexOf("/");
			this.currentDir = this.currentDir.substring(0, n);
			response("250 CWD command succesful");
		} else if (s.equals("PWD")) { // 打印当前目录
			response("257 \"" + this.currentDir + "\" is current dir.");
		} else if (s.startsWith("PORT ")) {
			// 记录端口
			String[] params = getParam(s, "PORT ").split(",");
			if (params.length <= 4 || params.length >= 7)// 限定端口号为4至7位
				response("500 command param error.");
			else {
				this.host = params[0] + "." + params[1] + "." + params[2] + "."
						+ params[3];// 客户端IP
				String port1 = null;
				String port2 = null;
				if (params.length == 6) {
					port1 = params[4];
					port2 = params[5];
				} else {
					port1 = "0";
					port2 = params[4];
				}
				this.port = Integer.parseInt(port1) * 256
						+ Integer.parseInt(port2);
				response("200 command successful.");
			}
		} else if (s.equals("PASV")) { // 进入被动模式
			if (pasvSocket != null)
				pasvSocket.close();
			try {
				pasvSocket = new ServerSocket(0);
				int pPort = pasvSocket.getLocalPort();
				String s_port;
				if (pPort <= 255)
					s_port = "255";
				else {
					int p1 = pPort / 256;
					int p2 = pPort - p1 * 256;
					s_port = p1 + "," + p2;
				}
				pasvSocket.setSoTimeout(60000);
				response("200 Entering Passive Mode ("
						+ InetAddress.getLocalHost().getHostAddress().replace(
								'.', ',') + "," + s_port + ")");
			} catch (Exception e) {
				if (pasvSocket != null) {
					pasvSocket.close();
					pasvSocket = null;
				}
			}
		} else if (s.startsWith("RETR")) { // 下载文件
			String file = currentDir + (currentDir.endsWith("/") ? "" : "/")
					+ getParam(s, "RETR");
			System.out.println("download file: " + file);
			Socket dataSocket;
			// 根据上一次的PASV或PORT命令决定使用哪个socket
			if (pasvSocket != null)
				dataSocket = pasvSocket.accept();
			else
				dataSocket = new Socket(this.host, this.port);
			OutputStream dos = null;
			InputStream fis = null;
			response("150 Opening ASCII mode data connection.");
			try {
				fis = new BufferedInputStream(new FileInputStream(
						translatePath(file)));
				dos = new DataOutputStream(new BufferedOutputStream(dataSocket
						.getOutputStream()));
				// 开始正式发送数据:
				byte[] buffer = new byte[20480]; // 发送缓冲 20k
				int num = 0; // 发送一次读取的字节数
				do {
					num = fis.read(buffer);
					if (num != (-1)) {
						// 发送：
						dos.write(buffer, 0, num);
						dos.flush();
					}
				} while (num != (-1));
				fis.close();
				fis = null;
				dos.close();
				dos = null;
				dataSocket.close();
				dataSocket = null;
				response("226 transfer complete."); // 响应一个成功标志
			} catch (Exception e) {
				response("550 ERROR: File not found or access denied.");
			} finally {
				try {
					if (fis != null)
						fis.close();
					if (dos != null)
						dos.close();
					if (dataSocket != null)
						dataSocket.close();
				} catch (Exception e) {
				}
			}
		}

		else if (s.startsWith("STOR")) { // 上传文件
			String file = currentDir + (currentDir.endsWith("/") ? "" : "/")
					+ getParam(s, "RETR");
			System.out.println("download file: " + file);
			Socket dataSocket;
			// 根据上一次的PASV或PORT命令决定使用哪个socket
			if (pasvSocket != null)
				dataSocket = pasvSocket.accept();
			else
				dataSocket = new Socket(this.host, this.port);
			OutputStream fis = null;
			InputStream dos = null;
			response("150 Opening ASCII mode data connection.");
			try {
				fis = new BufferedOutputStream(new FileOutputStream(
						translatePath(file)));
				dos = new DataInputStream(new BufferedInputStream(dataSocket
						.getInputStream()));
				// 开始正式发送数据:
				byte[] buffer = new byte[20480]; // 发送缓冲 20k
				int num = 0; // 发送一次读取的字节数
				while ((num = dos.read(buffer, 0, 1024)) != -1) {
					fis.write(buffer, 0, num);
					fis.flush();
				}// 上传到服务器

				fis.close();
				fis = null;
				dos.close();
				dos = null;
				dataSocket.close();
				dataSocket = null;
				response("226 transfer complete."); // 响应一个成功标志
			} catch (Exception e) {
				response("550 ERROR: File not found or access denied.");
			} finally {
				try {
					if (fis != null)
						fis.close();
					if (dos != null)
						dos.close();
					if (dataSocket != null)
						dataSocket.close();
				} catch (Exception e) {
				}
			}
		}

		else if (s.equals("LIST")) { // 列当前目录文件
			Socket dataSocket;
			// 根据上一次的PASV或PORT命令决定使用哪个socket
			if (pasvSocket != null)
				dataSocket = pasvSocket.accept();
			else
				dataSocket = new Socket(this.host, this.port);
			PrintWriter writer = new PrintWriter(new BufferedOutputStream(
					dataSocket.getOutputStream()));
			response("150 Opening ASCII mode data connection.");
			try {
				responseList(writer, this.currentDir);
				writer.close();
				dataSocket.close();
				response("226 transfer complete.");
			} catch (IOException e) {
				writer.close();
				dataSocket.close();
				response(e.getMessage());
			}
			dataSocket = null;
		} else {
			response("200 invalid command:" + s); // 没有匹配的命令，输出错误信息
		}
	}

	// 响应LIST命令
	private void responseList(PrintWriter writer, String path)
			throws IOException {
		File dir = new File(translatePath(path));
		if (!dir.isDirectory())
			throw new IOException("550 No such file or directory");
		File[] files = dir.listFiles();
		String dateStr;
		for (int i = 0; i < files.length; i++) {
			dateStr = new SimpleDateFormat("MMM dd hh:mm").format(new Date(
					files[i].lastModified()));
			if (files[i].isDirectory()) {
				writer.println("drwxrwxrwx  1 ftp      System            0 "
						+ dateStr + " " + files[i].getName());
			} else {
				writer.println("-rwxrwxrwx  1 ftp      System "
						+ getFileLength(files[i].length()) + " " + dateStr
						+ " " + files[i].getName());
			}
		}

		writer.println("total " + files.length);
		writer.flush();
	}

}